using System;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using HexTileBoard;
using System.Collections.Generic;

namespace ScreenManagement
{
    /// <summary>
    /// This screen implements the actual game logic. It is just a
    /// placeholder to get the idea across: you'll probably want to
    /// put some more interesting gameplay in here!
    /// </summary>
    class GameplayScreen : GameScreen
    {
        #region Fields

        NetworkSession networkSession;

        ContentManager content;
        SpriteFont gameFont;

        float pauseAlpha;

        Board board;
        PlayerIndex? blueIndex = null, redIndex = null, greenIndex = null, yellowIndex = null;
        bool blueAI = true, redAI = true, greenAI = false, yellowAI = false;
        BoardSize boardSize;

        #endregion

        #region Properties


        /// <summary>
        /// The logic for deciding whether the game is paused depends on whether
        /// this is a networked or single player game. If we are in a network session,
        /// we should go on updating the game even when the user tabs away from us or
        /// brings up the pause menu, because even though the local player is not
        /// responding to input, other remote players may not be paused. In single
        /// player modes, however, we want everything to pause if the game loses focus.
        /// </summary>
        new bool IsActive
        {
            get
            {
                if (networkSession == null)
                {
                    // Pause behavior for single player games.
                    return base.IsActive;
                }
                else
                {
                    // Pause behavior for networked games.
                    return !IsExiting;
                }
            }
        }


        #endregion

        #region Initialization

        /// <summary>
        /// Constructor.
        /// </summary>
        public GameplayScreen(NetworkSession networkSession, 
                    bool _blueIsAI, PlayerIndex? _blueIndex, bool _redIsAI, PlayerIndex? _redIndex,
                    bool _greenIsAI, PlayerIndex? _greenIndex, bool _yellowIsAI, PlayerIndex? _yellowIndex,
                    BoardSize _boardSize)
        {
            this.networkSession = networkSession;

            TransitionOnTime = TimeSpan.FromSeconds(1.5);
            TransitionOffTime = TimeSpan.FromSeconds(0.5);

            // Board variables
            blueAI = _blueIsAI;
            redAI = _redIsAI;
            greenAI = _greenIsAI;
            yellowAI = _yellowIsAI;
            blueIndex = _blueIndex;
            redIndex = _redIndex;
            greenIndex = _greenIndex;
            yellowIndex = _yellowIndex;
            boardSize = _boardSize;
        }


        /// <summary>
        /// Load graphics content for the game.
        /// </summary>
        public override void LoadContent()
        {
            if (content == null)
                content = new ContentManager(ScreenManager.Game.Services, "Content");

            // Create the board
            board = new Board(this.ScreenManager.Game, null, (short)boardSize, -1, blueAI, blueAI ? PlayerIndex.One : blueIndex, redAI, redAI ? PlayerIndex.Two : redIndex, greenAI, greenAI ? PlayerIndex.Three : greenIndex, yellowAI, yellowAI ? PlayerIndex.Four : yellowIndex);
            board.Initialize();
            board.LoadContent();

            this.gameFont = content.Load<SpriteFont>("Fonts/gameFont");

            // once the load has finished, we use ResetElapsedTime to tell the game's
            // timing mechanism that we have just finished a very long frame, and that
            // it should not try to catch up.
            ScreenManager.Game.ResetElapsedTime();
        }


        /// <summary>
        /// Unload graphics content used by the game.
        /// </summary>
        public override void UnloadContent()
        {
            if (board != null)
            {
                board.UnloadContent();
                board = null;
            }

            content.Unload();
        }


        #endregion

        #region Update and Draw


        /// <summary>
        /// Updates the state of the game.
        /// </summary>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
                                                       bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, false);

            // Gradually fade in or out depending on whether we are covered by the pause screen.
            if (coveredByOtherScreen)
                pauseAlpha = Math.Min(pauseAlpha + 1f / 32, 1);
            else
                pauseAlpha = Math.Max(pauseAlpha - 1f / 32, 0);

            if (IsActive)
            {
                board.Update(gameTime);
            }

            // If we are in a network game, check if we should return to the lobby.
            if ((networkSession != null) && !IsExiting)
            {
                if (networkSession.SessionState == NetworkSessionState.Lobby)
                {
                    LoadingScreen.Load(ScreenManager, true, null,
                                       new BackgroundScreen(),
                                       new LobbyScreen(networkSession));
                }
            }
        }


        /// <summary>
        /// Lets the game respond to player input. Unlike the Update method,
        /// this will only be called when the gameplay screen is active.
        /// </summary>
        public override void HandleInput(GameTime _gameTime, InputState input)
        {
            if (input == null)
                throw new ArgumentNullException("input");

            if (ControllingPlayer.HasValue)
            {
                if (board.gameOver)
                {
                    PlayerIndex pi;
                    if (input.IsNewKeyPress(Keys.A, null, out pi) || input.IsNewButtonPress(Buttons.A, null, out pi))
                    {
                        board.restarting();
                        board = new Board(this.ScreenManager.Game, null, (short)boardSize, -1, blueAI, blueAI ? PlayerIndex.One : blueIndex, redAI, redAI ? PlayerIndex.Two : redIndex, greenAI, greenAI ? PlayerIndex.Three : greenIndex, yellowAI, yellowAI ? PlayerIndex.Four : yellowIndex);
                        board.Initialize();
                        board.LoadContent();
                    }
                    else if (input.IsNewKeyPress(Keys.B, null, out pi) || input.IsNewButtonPress(Buttons.B, null, out pi))
                    {
                        board.UnloadContent();
                        board = null;
                        LoadingScreen.Load(ScreenManager, false, null, new BackgroundScreen(), new StartScreen());
                    }
                }
                else
                {
                    // Controling player can always exit
                    if (input.IsPauseGame(this.ControllingPlayer))
                    {
                        ScreenManager.AddScreen(new PauseMenuScreen(null), this.ControllingPlayer);
                        return;
                    }

                    // In local games, handle input for the active players.
                    foreach (KeyValuePair<PlayerColor, Player> p in board.Players)
                    {
                        // In the PC, we must exit after handling the current player, to prevent duplicate commands being issued
                        if (p.Value == board.currentPlayer)
                        {
                            HandlePlayerInput(_gameTime, input, p.Value.playerIndex);
                            return;
                        }
                    }
                }
            }
        }


        /// <summary>
        /// Handles input for the specified player. In local game modes, this is called
        /// just once for the controlling player. In network modes, it can be called
        /// more than once if there are multiple profiles playing on the local machine.
        /// Returns true if we should continue to handle input for subsequent players,
        /// or false if this player has paused the game.
        /// </summary>
        bool HandlePlayerInput(GameTime _gameTime, InputState _input, PlayerIndex _playerIndex)
        {
            // The game pauses either if the user presses the pause button, or if
            // they unplug the active gamepad. This requires us to keep track of
            // whether a gamepad was ever plugged in, because we don't want to pause
            // on PC if they are playing with a keyboard and have no gamepad at all!
            bool gamePadDisconnected = !_input.CurrentGamePadStates[(int)_playerIndex].IsConnected &&
                                       _input.GamePadWasConnected[(int)_playerIndex];
            //if (gamePadDisconnected)
            //{
            //    ScreenManager.AddScreen(new MessageBoxScreen("Please reconnect controller " + _playerIndex.ToString() + " to continue..."), _playerIndex);
            //}
            if (_input.IsPauseGame(_playerIndex))
            {
                ScreenManager.AddScreen(new PauseMenuScreen(networkSession), _playerIndex);
                return false;
            }

            if (!board.currentPlayer.IsAI)
            {
                board.HandlePlayerInput(_gameTime, _input, _playerIndex);
            }

            return true;
        }


        /// <summary>
        /// Draws the gameplay screen.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            // This game has a blue background. Why? Because!
            ScreenManager.GraphicsDevice.Clear(ClearOptions.Target,
                                               Color.CornflowerBlue, 0, 0);

            if (board != null)
                board.Draw(gameTime);

            // Our player and enemy are both actually just text strings.
            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;

            spriteBatch.Begin();

            if (networkSession != null)
            {
                string message = "Players: " + networkSession.AllGamers.Count;
                Vector2 messagePosition = new Vector2(100, 480);
                spriteBatch.DrawString(gameFont, message, messagePosition, Color.White);
            }

            spriteBatch.End();

            // If the game is transitioning on or off, fade it out to black.
            if (TransitionPosition > 0 || pauseAlpha > 0)
            {
                float alpha = MathHelper.Lerp(1f - TransitionAlpha, 1f, pauseAlpha / 2);

                ScreenManager.FadeBackBufferToBlack(alpha);
            }
        }

        #endregion
    }
}
